package com.tomato.modules.id.po;

import com.tomato.api.id.dataobject.TomatoIdDO;
import com.tomato.modules.id.constants.TomatoIdConstants;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.atomic.AtomicLong;

/**
 * 号码段
 *
 * @author lizhifu
 * @date 2022/3/17
 */
@Setter
@Getter
@ToString
public class SegmentId {
    private static final Logger logger = LoggerFactory.getLogger(SegmentId.class);
    /**
     * 业务类型
     */
    private String bizType;
    /**
     * maxId = pre_maxId + step
     * 当前ID = 前最大ID + 步长
     */
    private long maxId;
    /**
     * currentId = maxId - step
     * 当前ID = 最大ID - 步长
     */
    private AtomicLong currentId;
    /**
     * 预加载的最大值 = 当前ID + 步长 * 预加载下个号段的百分比
     * 提前加载 nextId 增大效率
     */
    private long loadingId;
    /**
     * 检查是否已经达到 maxId
     * @return
     */
    public boolean checkMaxId() {
        return currentId.get() <= maxId;
    }
    /**
     * 检查是否已经达到 loadingId
     * @return
     */
    public boolean checkLoadingId() {
        return currentId.get() <= loadingId;
    }
    public Result nextId() {
        // 极端情况下，id 会增长到 maxId，此时直接获取新的 maxId，不在等待线程池
        // 例如线程池已经满了或者处理速度过慢，此时如果不先增加 currentId.addAndGet(1) 会造成死循环直到线程执行了。
        // 例如：step = 10 ， loadingId = currentId + 2(step(10) * 0.2)
        long id = currentId.addAndGet(1);
        // currentId + 1 > maxId
        if (!checkMaxId()) {
            logger.info("{}:获取 nextId 返回结果:{} currentId+1=:{} maxId={} loadingId={}",bizType,ResultCode.OVER,id,maxId,loadingId);
            return new Result(ResultCode.OVER, id);
        }
        // currentId + 1 > loadingId
        if (!checkLoadingId()) {
            logger.info("{}:获取 nextId 返回结果:{} currentId+1=:{} maxId={} loadingId={}",bizType,ResultCode.LOADING,id,maxId,loadingId);
            return new Result(ResultCode.LOADING, id);
        }
        logger.info("{}:获取 nextId 返回结果:{} currentId+1=:{} maxId={} loadingId={}",bizType,ResultCode.NORMAL,id,maxId,loadingId);
        return new Result(ResultCode.NORMAL, id);
    }

    public static SegmentId buildTomatoId(TomatoIdDO oldTomatoIdDO){
        Long newMaxId = oldTomatoIdDO.getMaxId() + oldTomatoIdDO.getStep();

        SegmentId segmentId = new SegmentId();
        segmentId.setBizType(oldTomatoIdDO.getBizType());
        segmentId.setCurrentId(new AtomicLong(newMaxId - oldTomatoIdDO.getStep()));
        segmentId.setMaxId(newMaxId);
        // 默认20%加载
        segmentId.setLoadingId(segmentId.getCurrentId().get() + oldTomatoIdDO.getStep() * TomatoIdConstants.LOADING_PERCENT / 100);

        logger.info("SegmentId buildTomatoId 结果:{}",segmentId);
        return segmentId;
    }
}
